﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using RSEngine.Math;
using RSRenderer.Core;

namespace RSRenderer.OptimizedGeometry
{

    /// <summary>
    /// Helper class intended to test the intersections.
    /// </summary>
    static public class IntersectionHelper
    {

        #region Ray / Box

        /// <summary>
        /// Tests an intersection between a ray and a box.
        /// </summary>
        /// <param name="ray"></param>
        /// <param name="box"></param>
        /// <returns>A Pair object containing whether the intersection occurred, and the distance between the 2 objects.</returns>
        public static Intersection Intersects(Ray ray, AxisAlignedBox box)
        {
            if (box.IsInvalid)
                return Intersection.NoIntersection;

            float lowt = 0.0f;
            float t;
            bool hit = false;
            Vector3 hitPoint;
            Vector3 min = box.Minimum;
            Vector3 max = box.Maximum;

            // check origin inside first
            if (ray.Origin > min && ray.Origin < max)
                return new Intersection(true, 0.0f);

            // check each face in turn, only check closest 3

            // Min X
            if (ray.Origin.x < min.x && ray.Direction.x > 0)
            {
                t = (min.x - ray.Origin.x) / ray.Direction.x;

                if (t > 0)
                {
                    // substitue t back into ray and check bounds and distance
                    hitPoint = ray.Origin + ray.Direction * t;

                    if (hitPoint.y >= min.y && hitPoint.y <= max.y &&
                        hitPoint.z >= min.z && hitPoint.z <= max.z &&
                        (!hit || t < lowt))
                    {

                        hit = true;
                        lowt = t;
                    }
                }
            }

            // Max X
            if (ray.Origin.x > max.x && ray.Direction.x < 0)
            {
                t = (max.x - ray.Origin.x) / ray.Direction.x;

                if (t > 0)
                {
                    // substitue t back into ray and check bounds and distance
                    hitPoint = ray.Origin + ray.Direction * t;

                    if (hitPoint.y >= min.y && hitPoint.y <= max.y &&
                        hitPoint.z >= min.z && hitPoint.z <= max.z &&
                        (!hit || t < lowt))
                    {

                        hit = true;
                        lowt = t;
                    }
                }
            }

            // Min Y
            if (ray.Origin.y < min.y && ray.Direction.y > 0)
            {
                t = (min.y - ray.Origin.y) / ray.Direction.y;

                if (t > 0)
                {
                    // substitue t back into ray and check bounds and distance
                    hitPoint = ray.Origin + ray.Direction * t;

                    if (hitPoint.x >= min.x && hitPoint.x <= max.x &&
                        hitPoint.z >= min.z && hitPoint.z <= max.z &&
                        (!hit || t < lowt))
                    {

                        hit = true;
                        lowt = t;
                    }
                }
            }

            // Max Y
            if (ray.Origin.y > max.y && ray.Direction.y < 0)
            {
                t = (max.y - ray.Origin.y) / ray.Direction.y;

                if (t > 0)
                {
                    // substitue t back into ray and check bounds and distance
                    hitPoint = ray.Origin + ray.Direction * t;

                    if (hitPoint.x >= min.x && hitPoint.x <= max.x &&
                        hitPoint.z >= min.z && hitPoint.z <= max.z &&
                        (!hit || t < lowt))
                    {

                        hit = true;
                        lowt = t;
                    }
                }
            }

            // Min Z
            if (ray.Origin.z < min.z && ray.Direction.z > 0)
            {
                t = (min.z - ray.Origin.z) / ray.Direction.z;

                if (t > 0)
                {
                    // substitue t back into ray and check bounds and distance
                    hitPoint = ray.Origin + ray.Direction * t;

                    if (hitPoint.x >= min.x && hitPoint.x <= max.x &&
                        hitPoint.y >= min.y && hitPoint.y <= max.y &&
                        (!hit || t < lowt))
                    {

                        hit = true;
                        lowt = t;
                    }
                }
            }

            // Max Z
            if (ray.Origin.z > max.z && ray.Direction.z < 0)
            {
                t = (max.z - ray.Origin.z) / ray.Direction.z;

                if (t > 0)
                {
                    // substitue t back into ray and check bounds and distance
                    hitPoint = ray.Origin + ray.Direction * t;

                    if (hitPoint.x >= min.x && hitPoint.x <= max.x &&
                        hitPoint.y >= min.y && hitPoint.y <= max.y &&
                        (!hit || t < lowt))
                    {

                        hit = true;
                        lowt = t;
                    }
                }
            }

            if (!hit)
                return Intersection.NoIntersection;

            return new Intersection(hit, lowt);
        }

        #endregion

    }

}
